import json
import sys
import collections
import threading

from cqtapi import QtApiWrapper
from model import Order
from response_utils import *
from api import print
import queue
from six import exec_
from globalval import globalVal
from globalval import log, init_log
import utils, datetime
import enum_type
from enum_type import (
    future_direction_dic, entrustdirection_dic
)
from utils import (format_exception, debug_print_prefix, error_print_prefix,
                   get_minute_time, get_afternoon_fifteen_clock, get_stock_sleep_time,
                   get_morning_nine_half_clock)
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.triggers.cron import CronTrigger


def comp(algoscript, algo_filename):
    return compile(algoscript, algo_filename, 'exec')


def exec(code, namespace):
    exec_(code, namespace)


def noop(*args, **kwargs):
    pass


class TraderApiPy(QtApiWrapper):

    def __init__(self, algo_file=None):
        self.guid = None
        self.reqid_order_id_dict = {}
        self.call_back_queues = {}
        self.orderid_entrustno_map = {}
        self._init_call_back_queue()
        self.investunit_id = 0
        self.instance_id = 0
        self.frequency = 'tick'

        self.namespace = {}
        self._initialize = None
        self.context = None
        self._on_trade_resp = None
        self._on_entrust_resp = None
        self._handle_tick = None
        self._handle_data = None
        self.subscribe_list = []
        # basic info
        self.symbol_info_dict = {}

        with open(algo_file, 'r', encoding='utf-8-sig') as f:
            algo_text = f.read()
            algo_text = algo_text.replace('IQEngine', 'qtapi')
            code = comp(algo_text, algo_file)
            exec(code, self.namespace)
            self._initialize = self.namespace.get('initialize', noop)
            self.context = self.namespace.get('context', noop)
            self._on_trade_resp = self.namespace.get('on_trade_response', noop)
            self._on_entrust_resp = self.namespace.get('on_order_response', noop)
            self._handle_data = self.namespace.get('handle_data', noop)
            self._handle_tick = self.namespace.get('handle_tick', noop)

    def set_default_investid_instanceid(self, investunit_id, instance_id):
        self.investunit_id = int(investunit_id)
        self.instance_id = int(instance_id)

    def set_universe(self, security_list):
        if isinstance(security_list, str):
            security_list = [security_list]
        self.subscribe_list = list(set(security_list))
        self.subscribe(self.subscribe_list)

    def set_frequency(self, frequency='tick'):
        self.frequency = frequency

    def _init_call_back_queue(self):
        for name, function_id in function_name_id_dict.items():
            if name in (FunctionIds.SUBSCRIBE, FunctionIds.SNAPSHOT, FunctionIds.ENTRUST_RESPONSE,
                        FunctionIds.TRADE_RESPONSE, FunctionIds.M1_SUBSCRIBE):
                continue
            self.call_back_queues[function_id] = queue.Queue()

    def construct_params(self, functionid, type, fields, data):
        base_params = {"message": {
            "head": {"user": "", "timestamp": "", "guid": self.guid, "type": None,
                     "functionid": None},
            "body": {"fields": None, "data": None}}}
        base_params['message']['head']['functionid'] = functionid
        base_params['message']['head']['type'] = type
        base_params['message']['body']['fields'] = fields
        base_params['message']['body']['data'] = data
        return base_params

    def unpack_data(self, response_dict):
        if response_dict['message']['body']['error_no'] != 0:
            sys.stderr.write('unpack_data出错，error_info: %s' % response_dict['message']['body']['error_info'])
            if response_dict['message']['head']['functionid'] not in [FunctionIds.ENTRUST_RESPONSE]:
                return {}

        fields = response_dict['message']['body']['fields']
        data = response_dict['message']['body']['data']
        head = response_dict['message']['head']
        error_info = [response_dict['message']['body']['error_no'],
                      response_dict['message']['body']['error_info']]
        return {'fields': fields, 'data': data, 'head': head, 'error_info': error_info}

    def dict_to_str(self, request_params):
        request_content = str(request_params) + '\r\n'
        return request_content

    def make_request_no_recv(self, functionid, type, fields, data):
        while not self.guid:
            print(debug_print_prefix + ' waiting to initializing')
            time.sleep(0.1)
        request_params = self.construct_params(functionid, type, fields, data)
        request_params_str = self.dict_to_str(request_params)
        r = request_params_str.encode(encoding='utf_8', errors='strict')
        self.SendRequest(r)

    def Init(self, port) -> None:
        super(TraderApiPy, self).Init(port)
        self._initialize(self)

    def Release(self, ):
        pass

    def OnMsg(self, buf) -> None:
        s = buf.decode('utf-8')
        if '990001' in s:
            j = json.loads(s)
            if j['message']['body']['error_no'] == 0 and 990001 == j['message']['head']['functionid']:
                self.guid = j['message']['head']['user']
                print(debug_print_prefix + '  got guid', self.guid)
                return
        lines = buf.decode(encoding='utf_8', errors='strict').split("\r\n")
        for line in lines:
            if not line.strip():
                continue
            try:
                resp = self.unpack_data(json.loads(line))
                function_id = resp['head']['functionid']
                if function_id in [98769876, ]:
                    print(error_print_prefix + ['body']['error_info'])
                    continue
                else:
                    self._route(resp)
            except Exception as e:
                pass

    def _get_next_message(self, function_id):
        count = 1
        while True:
            if not self.call_back_queues[function_id].empty():
                data = self.call_back_queues[function_id].get()
                return data
            time.sleep(0.01)
            count += 1
            if count >= 1000:
                return None

    @format_exception
    def _route(self, data):
        print(debug_print_prefix + '  call back FunctionId:{} head={} data={}'.format(data['head']['functionid'],
                                                                                      data['head'], data))
        function_name_id = dict([(name, _id) for name, _id in FunctionIds.__dict__.items() if isinstance(_id, int)])
        valid_function_ids = function_name_id.values()
        function_id = data['head']['functionid']
        if function_id == FunctionIds.SNAPSHOT:
            snapshot = convert_from_snapshot(data)
            self.on_tick(snapshot)
        elif function_id == FunctionIds.M1_SUBSCRIBE:
            data = convert_from_bar(data)
            self.symbol_info_dict = data
        elif function_id == FunctionIds.ENTRUST_RESPONSE:
            ret_dict = convert_entrust_response(data)
            # ret_dict['order_id'] = self.reqid_order_id_dict.get(ret_dict.get('strategy_req_id', -1))
            third_remark = ret_dict['third_remark']
            if '#' in third_remark:
                third_remark = third_remark[:third_remark.index('#')]
            ret_dict['order_id'] = third_remark
            self.orderid_entrustno_map[ret_dict['order_id']] = ret_dict['entrust_no']
            self.on_entrust(ret_dict, data['error_info'])
        elif function_id == FunctionIds.TRADE_RESPONSE:
            ret_list = convert_trade_responnse(data)
            self.on_deal(ret_list)
        elif function_id in valid_function_ids:
            self.call_back_queues[function_id].put_nowait(data)
        else:
            sys.stderr.write("unknown function_id:{} data:{}".format(function_id, data))

    @format_exception
    def on_tick(self, snapshot):
        self._handle_tick(self, snapshot)

    def minute_job(self):
        now = datetime.datetime.now()
        dt_str = now.strftime("%Y%m%d%H%M%S")
        dt = datetime.datetime.strptime(dt_str, "%Y%m%d%H%M%S")
        if get_morning_nine_half_clock() < get_minute_time(dt_str) < get_afternoon_fifteen_clock() \
                and not get_stock_sleep_time():
            stock_list = [security for security in self.subscribe_list if
                          self.symbol_info_dict[security]['stock_status'] == '1']
            code_df_dict = self.get_latest_history(1, frequency='1m', security_list=stock_list)
            code_bar_dict = {}
            for code, df in code_df_dict.items():
                bar = Bar()
                first_row = df.iloc[0]
                bar.symbol = code
                bar.datetime = first_row.datetime
                bar.open = first_row.open_price
                bar.high = first_row.high_price
                bar.low = first_row.low_price
                bar.close = first_row.close_price
                bar.volume = first_row.amount
                bar.balance = first_row.balance
                code_bar_dict[code] = bar
            self.on_m1_data(code_bar_dict)

    def minute_bar_scheduler(self, ):
        scheduler = BlockingScheduler()
        trigger = CronTrigger(day_of_week='mon-fri', hour='09-22', minute="*/1", second="0")
        scheduler.add_job(self.minute_job, trigger)  # 根据需要进行设置
        scheduler.start()

    def do_1m_scheduler(self):
        threading.Thread(target=self.minute_bar_scheduler, daemon=True).start()

    @format_exception
    def on_m1_data(self, bar):
        self._handle_data(self, bar)

    @format_exception
    def on_entrust(self, ret_dict, error_no_info):
        os = OrderStatus()
        os.entrust_no = ret_dict.get("entrust_no", None)
        os.filled_amount = ret_dict.get("filled_amount", None)
        os.price = ret_dict.get("price", None)
        os.status = ret_dict.get("status", None)
        os.symbol = ret_dict.get("stock_code", "") + "." + ret_dict.get("market_no", "")
        os.datetime = ret_dict.get("datetime", None)
        os.entrust_direction = ret_dict.get("entrust_direction", None)
        os.futures_direction = ret_dict.get("futures_direction", None)
        os.order_id = ret_dict.get("order_id", None)
        os.amount = ret_dict.get("amount", None)
        os.is_error = ret_dict.get("is_error", len(error_no_info) > 1 and error_no_info[0] != 0)
        os.message = "" if len(error_no_info) < 2 else error_no_info[1]
        os.close_direction = ret_dict.get("close_direction", None)
        os.withdraw_amount = ret_dict.get("withdraw_amount", None)
        os.investunit_id = ret_dict.get("investunit_id", None)
        os.instance_id = ret_dict.get("instance_id", None)
        self._on_entrust_resp(self, os)

    @format_exception
    def on_deal(self, deal_dict):
        ts = TradeStatus()
        ts.order_id = deal_dict.get("order_id", None)
        ts.deal_time = deal_dict.get("deal_time", None)
        ts.symbol = deal_dict.get("stock_code", "") + "." + deal_dict.get("market_no", "")
        ts.amount = deal_dict.get("filled", None)
        ts.price = deal_dict.get("price", None)
        ts.entrust_direction = deal_dict.get("entrust_direction", None)
        ts.futures_direction = deal_dict.get("futures_direction", None)
        ts.investunit_id = deal_dict.get("investunit_id", None)
        ts.instance_id = deal_dict.get("instance_id", None)
        ts.close_direction = deal_dict.get("close_direction", None)
        ts.deal_no = deal_dict.get("deal_no", None)
        self._on_trade_resp(self, ts)

    # start function
    def subscribe(self, symbols: list):
        symbols = list(set(symbols))
        for symbol in symbols:
            symbol = symbol.strip().upper()
            if len(symbol.strip()) != len("000001.XSHE") or '.' not in symbol \
                    or (symbol[symbol.index('.') + 1:] not in ('XSHE', 'XSHG')):
                print("订阅行情代码:{}格式错误，请输入如：000001.XSHE 或者 600000.XSHG".format(symbol))
            print(debug_print_prefix + '  subscribe:{} '.format(symbol))
            if self.frequency == 'tick':
                self.make_request_no_recv(FunctionIds.SUBSCRIBE, 3, ["contract_code"], [[symbol]])
            else:
                self.make_request_no_recv(FunctionIds.M1_SUBSCRIBE, 5, ["code_list"], [[symbol]])

    @format_exception
    def order_basket(self, entrust_list: list) -> list:
        data_list = []
        order_id_list = []
        fields = None
        for entrust in entrust_list:
            investunit_id = int(entrust.get('investunit_id', "0"))
            instance_id = int(entrust.get('instance_id', "0"))
            if investunit_id == 0 or instance_id == 0:
                investunit_id = self.investunit_id
                instance_id = self.instance_id
            order = Order().create(investunit_id, instance_id, entrust['symbol'], entrust['price'],
                                   entrust['amount'], entrust['entrust_direction'], price_type=PriceType.MARKET_ORDER,
                                   futures_direction=None)
            fields, data = order.build_order()
            data_list.append(data[0])
            self.orderid_entrustno_map[order.order_id] = None
            self.reqid_order_id_dict[order.strategy_req_id] = order.order_id
            order_id_list.append(order.order_id)
        self.make_request_no_recv(FunctionIds.PLACE_ORDER, 1, fields, data_list)
        return order_id_list

    def order(self, security: str, amount: int, limit_price=None, price_type='A',
              entrust_direction="BUY", investunit_id=0, instance_id=0, futures_direction=None) -> str:
        """
        下单函数
        :param investunit_id: 账户ID
        :param instance_id: 投资组合ID
        :param security: 600000.XSHG
        :param amount: 委托数量
        :param limit_price: 委托价格
        :param price_type: 市价委托类型
        :param entrust_direction: 委托方向[BUY | SELL]
        :return: 自定义委托ID
        """
        if investunit_id == 0 or instance_id == 0:
            order = Order().create(self.investunit_id, self.instance_id, security, limit_price, amount,
                                   entrust_direction, price_type=PriceType.MARKET_ORDER,
                                   futures_direction=futures_direction)
        else:
            order = Order().create(investunit_id, instance_id, security, limit_price, amount, entrust_direction,
                                   price_type=PriceType.MARKET_ORDER, futures_direction=futures_direction)
        self.__place_order(order)
        return order.order_id

    def __place_order(self, order_obj: Order):
        fields, data = order_obj.get_order()
        self.orderid_entrustno_map[order_obj.order_id] = None
        self.reqid_order_id_dict[order_obj.strategy_req_id] = order_obj.order_id
        self.make_request_no_recv(FunctionIds.PLACE_ORDER, 1, fields, data)

    def cancel_order_by_entrust_no(self, entrust_no: int):
        fields = ['entrust_code']
        data = [[entrust_no]]
        self.make_request_no_recv(FunctionIds.CANCEL_ORDER, 1, fields, data)

    def cancel_order(self, order_id: str):
        if order_id in self.orderid_entrustno_map:
            entrust_no = self.orderid_entrustno_map[order_id]
            self.cancel_order_by_entrust_no(entrust_no)
        else:
            print(error_print_prefix + "找不到order_id(" + order_id + ")对应的委托编号")

    def get_snapshot(self, contract_list):
        fields = ['contract_code']
        data = [[e] for e in contract_list]
        self.make_request_no_recv(FunctionIds.GET_SNAPSHOT, 3, fields, data)
        try:
            data = convert_from_snapshot(self._get_next_message(FunctionIds.GET_SNAPSHOT))
            return data
        except Exception as e:
            print(traceback.format_exc())
            return {}

    def get_position(self, investunit_id=0, instance_id=0, position_type='STOCK') -> pd.DataFrame:
        """
        获取当前持仓
        :param investunit_id: 账户ID
        :param instance_id: 投资组合ID
        :return: pd.DataFrame
        """
        columns = ['market_no', 'current_amount', 'current_cost', 'cost_price', 'position_type',
                   'enable_amount', 'invest_type', 'stock_type', 'investunit_id', 'instance_id']
        fields = ["scheme_id", "instance_id", "investunit_id", "position_type"]

        if investunit_id == 0 or instance_id == 0:
            data = [[1195, self.instance_id, self.investunit_id, position_type]]
        else:
            data = [[1195, instance_id, investunit_id, position_type]]
        try:
            self.make_request_no_recv(FunctionIds.GET_POSITION, 5, fields, data)
            data = self._get_next_message(FunctionIds.GET_POSITION)
            return convert_get_position_response(data, columns)
        except Exception as e:
            return pd.DataFrame([], columns=columns)

    def get_opinstance(self, investunit_id=0, instance_id=0):
        try:
            self.make_request_no_recv(FunctionIds.GET_OP_INSTANCE, 5, ['scheme_id'], [[1195]])
            data = self._get_next_message(FunctionIds.GET_OP_INSTANCE)
            return convert_response_to_df(data)
        except Exception as e:
            return []

    def encode(self, request_str):
        return request_str.encode(encoding='utf_8', errors='strict')

    def test_build(self, ):
        self.BuildAndSendRequest(FunctionIds.GET_OP_INSTANCE, 5, self.encode("['scheme_id']"),
                                 self.encode("[[1195]]"))
        data = self._get_next_message(FunctionIds.GET_OP_INSTANCE)
        return convert_response_to_df(data)

    def get_ext_orders(self, security=None, investunit_id=0, instance_id=0, request_num=100,
                       query_type='STOCK', entrust_state=None, is_order_by_python=False) -> pd.DataFrame:
        """
        查询投资组合的委托流水
        :param investunit_id:
        :param instance_id:
        :param request_num:
        :param query_type:
        :return: pd.DataFrame column如下：
        """
        columns = ['stock_code', 'entrust_price', 'entrust_amount', 'entrust_date',
                   'entrust_time', 'entrust_direction', 'futures_direction',
                   'close_direction', 'futu_invest_type', 'price_type', 'entrust_state',
                   'entrust_state_cn', 'investunit_id',
                   'instance_id', 'withdraw_amount', 'ext_remark', 'total_deal_amount']
        df = pd.DataFrame()
        request_num = min(request_num, 10000)
        if request_num < 0 or not isinstance(request_num, int):
            sys.stderr.write("请求数量必须为正数，请重新输入！")
            return df
        fields = ['investunit_id', 'instance_id', 'request_num', 'position_str', 'entrust_state', 'is_order_by_python']
        position_str = '' if security is None else security
        entrust_state = entrust_state if entrust_state else ''
        if investunit_id == 0 or instance_id == 0:
            data = [[self.investunit_id, self.instance_id, request_num, position_str, entrust_state,
                     str(is_order_by_python)]]
        else:
            data = [[investunit_id, instance_id, request_num, position_str, entrust_state, str(is_order_by_python)]]
        try:
            self.make_request_no_recv(FunctionIds.GET_EXT_ORDERS, 5, fields, data)
            data = self._get_next_message(FunctionIds.GET_EXT_ORDERS)
            return convert_get_ext_orders_response(data, columns)
        except Exception as e:
            print(error_print_prefix + str(e))
            return pd.DataFrame([], columns)

    def get_ext_trades(self, security=None, investunit_id=0, instance_id=0, request_num=100, query_type='STOCK',
                       is_order_by_python=False) \
            -> pd.DataFrame:
        """
        :param security:
        :param investunit_id:
        :param instance_id:
        :param request_num:
        :param query_type:
        :return: pd.DataFrame column如下
        """
        columns = ['stock_code', 'deal_price', 'deal_amount', 'entrust_no',
                   'entrust_direction', 'futures_direction', 'deal_date', 'deal_time',
                   'deal_balance', 'investunit_id', 'instance_id', 'futu_invest_type']
        df = pd.DataFrame()
        request_num = min(request_num, 10000)
        if request_num < 0 or not isinstance(request_num, int):
            print(error_print_prefix + " 请求数量必须为正数，请重新输入！")
            return df

        fields = ["investunit_id", "instance_id", "request_num", "position_str", "is_order_by_python"]
        position_str = '' if security is None else security
        if investunit_id == 0 or instance_id == 0:
            data = [[self.investunit_id, self.instance_id, request_num, position_str, str(is_order_by_python)]]
        else:
            data = [[investunit_id, instance_id, request_num, position_str, str(is_order_by_python)]]
        try:
            self.make_request_no_recv(FunctionIds.GET_EXT_TRADES, 5, fields, data)
            data = self._get_next_message(FunctionIds.GET_EXT_TRADES)
            return convert_get_ext_trades_response(data, columns)
        except Exception as e:
            return df

    @format_exception
    def get_his_orders(self, begin_date, end_date, investunit_id=0, position_int=0, request_num=5000):
        print('Enter get_his_orders!')
        if investunit_id == 0:
            investunit_id = self.investunit_id

        if request_num < 0:
            raise Exception("请求数量必须为正数，请重新输入！")
        if not isinstance(request_num, int):
            raise Exception("请求数量必须为整数，请重新输入！")

        df = pd.DataFrame([],
                          columns=['stock_code', 'market_no', 'full_code', 'entrust_price', 'entrust_amount',
                                   'entrust_date', 'entrust_time', 'entrust_no', 'entrust_direction',
                                   'futures_direction',
                                   'close_direction', 'futu_invest_type', 'price_type', 'entrust_state',
                                   'investunit_id',
                                   'instance_id', 'withdraw_amount', 'ext_remark'])

        page_request = 5000  # 单页请求条数
        if request_num < page_request:
            # 如果总的请求数量少于单页请求数量，那么单页请求数量为总的请求数量
            page_request = request_num

        while request_num > 0:
            self.make_request_no_recv(FunctionIds.GET_HIS_ORDERS, 5,
                                      ["begin_date", "end_date", "investunit_id", "position_int",
                                       'request_num'],
                                      [[begin_date, end_date, investunit_id, position_int, page_request]])
            respons = self._get_next_message(FunctionIds.GET_HIS_ORDERS)
            if len(respons) == 0 or len(respons['data']) == 0:
                break

            df_temp = covert_his_entrust_data(respons)
            df = pd.concat([df, df_temp], join='inner')
            position_int = respons['data'][-1][respons['fields'].index('position_int')]  # 定位串
            temp_count = len(df_temp)
            if temp_count < page_request:
                break
            request_num = request_num - temp_count
            if request_num < page_request:
                # 剩余数量不足一次请求的数量
                page_request = request_num

        df.index = df['entrust_no']
        return df.sort_values(by=['entrust_date', 'entrust_time'], ascending=False)

    @format_exception
    def get_trade_time(self, code):
        code = code.upper()
        self.make_request_no_recv(FunctionIds.GET_TRADE_TIME, 3, ["contract_code"], [[code]])
        part = self._get_next_message(FunctionIds.GET_TRADE_TIME)
        data_part = part['data']

        if data_part:
            trader_1 = data_part[0][0][0]
            trader_list_1 = []
            for i in trader_1:
                trader_list_1.append([i['OpenTime'], i['CloseTime']])
            return trader_list_1
        else:
            return []

    @format_exception
    def get_available_money(self, investunit_id=0, investunit_type='STOCK', market_no=1):
        if investunit_id == 0:
            investunit_id = self.investunit_id
        self.make_request_no_recv(FunctionIds.GET_AVAILABLE_MONEY, 5, ["investunit_id", 'market_no', 'investunit_type'],
                                  [[investunit_id, market_no, investunit_type]])
        data = self._get_next_message(FunctionIds.GET_AVAILABLE_MONEY)
        if len(data['data']):
            return {'available_money': data['data'][0][0][0],
                    'available_margin': data['data'][0][1][0]}
        else:
            return {'available_money': 0.0, 'available_margin': 0.0}

    def get_min_price_interval(self, code_list=None):
        if code_list is None:
            raise Exception("输入参数为空")
        if isinstance(code_list, str):
            code_list = [code_list]
        if code_list:
            self.make_request_no_recv(FunctionIds.GET_CONTRACT_INFO, 3, ["contract_code"], [[code_list]])
            part = self._get_next_message(FunctionIds.GET_CONTRACT_INFO)
            data_part = part['data']
            if data_part:
                data = data_part[0][0][0]
                dic = dict(zip(code_list, data))
                return dic

            else:
                return {}
        else:
            return {}

    @format_exception
    def query_stock_fund(self, investunit_id=0) -> StockFund:
        """
        :param investunit_id: 投资单元ID
        :return: StockFund
        """
        columns = ['avaliable_money', 'begin_balance', 'stock_asset', 'stock_cost',
                   'total_float_profit', 'total_profit', 'position_ratio']
        invest_unit_id = investunit_id if investunit_id > 0 else self.investunit_id
        try:
            self.make_request_no_recv(FunctionIds.QUERY_STOCK_FUND, 5, ['investunit_id'], [[invest_unit_id]])
            data = self._get_next_message(FunctionIds.QUERY_STOCK_FUND)
            df = convert_response_to_df(data)
            stock_fund = StockFund()
            if len(df) < 1:
                return stock_fund
            first_row = df.iloc[0]
            stock_fund.avaliable_money = first_row.avaliable_money
            stock_fund.stock_cost = first_row.stock_cost
            stock_fund.stock_asset = first_row.stock_asset
            stock_fund.total_profit = first_row.total_profit
            stock_fund.total_float_profit = first_row.total_float_profit
            stock_fund.begin_balance = first_row.begin_balance
            stock_fund.position_ratio = first_row.position_ratio
            return stock_fund
        except Exception as e:
            print(debug_print_prefix, e)
            return StockFund()

    def get_margin_fund(self, investunit_id=0):
        """
        :param investunit_id: 投资单元ID
        :return: pd.DataFrame
        """
        columns = ['total_value', 'available_money', 'available_margin', 'total_float_profit',
                   'daily_profit', 'total_liability', 'market_value', 'net_value', 'maintenance_margin_rate',
                   'position_ratio']
        invest_unit_id = investunit_id if investunit_id > 0 else self.investunit_id
        try:
            self.make_request_no_recv(FunctionIds.GET_MARGIN_FUND, 5, ['investunit_id'], [[invest_unit_id]])
            data = self._get_next_message(FunctionIds.GET_MARGIN_FUND)
            df = convert_response_to_df(data)
            df['total_value'] = df['total_asset_money']
            df['available_money'] = df['enable_balance']
            df['available_margin'] = df['enable_deposit_balance']
            df['total_float_profit'] = df['float_profit_loss']
            df['daily_profit'] = df['today_profit_loss']
            df['total_liability'] = df['total_debit']
            df['market_value'] = df['total_market_value']
            df['net_value'] = df['net_assets']
            df['maintenance_margin_rate'] = df['assure_ratio']
            df['position_ratio'] = df['market_value'] / (0.1 + df['total_value'])
            return df[columns]
        except Exception as e:
            print(e)
            return pd.DataFrame([], columns)

    def __request(self, function_id, type, fields, data):
        self.make_request_no_recv(function_id, type, fields, data)
        return self._get_next_message(function_id)

    def get_latest_history(self, count=5, frequency='1d', security_list=None, fq='pre') -> collections.OrderedDict:
        """
        :param count: 数量
        :param frequency: 周期:[1d,1m]
        :param security_list: 股票池
        :param fq: 复权方式[ 'pre' | 'post' | None]
        :return: {'stock_code': pd.DataFrame}
        """
        columns = ['amount', 'balance', 'date', 'datetime', 'open_price', 'high_price',
                   'low_price', 'close_price', 'position', 'pre_close_price', 'time']
        res = collections.OrderedDict()
        try:
            fq = fq_map.get(fq, 0)
            fields = ['contract_code', 'kline_period', 'date', 'time', 'count', 'search_direction',
                      'right_recovery_mode']
            freq = kline_dictionary.get(FREQUENCY_DICT.get(frequency, frequency))
            for security in security_list:
                data = self.__request(FunctionIds.GET_LATEST_HISTORY, 3, fields, [[security, freq, 0, 0, count, 1, fq]])
                df = convert_response_to_df(data, columns)
                res[security] = df
            return res
        except Exception as e:
            print(e)
            return res

    def get_price_by_date(self, security_list=[], start_date='20150101', end_date='20151231',
                          frequency='1d', fields=None, fq=None) -> pd.DataFrame:
        """
        :param security_list: 股票池
        :param start_date: 字符串，开始时间，默认是'20150101'
        :param end_date:   字符串，结束时间，默认是'20151231'
        :param frequency: 周期 [ 1d | 1m | 5m | 30m ]
        :param fields:
        :param fq: 复权方式[ 'pre' | 'post' | None]
        :return: pd.DataFrame column如下
        """
        columns = ['amount', 'balance', 'date', 'datetime', 'open_price', 'high_price',
                   'low_price', 'close_price', 'position', 'pre_close_price', 'time']
        try:
            fq = fq_map.get(fq, 0)
            fields = ['contract_code', 'kline_period', 'startDate', 'endDate', 'right_recovery_mode']
            ret = collections.OrderedDict()
            freq = kline_dictionary.get(FREQUENCY_DICT.get(frequency, frequency))
            for security in security_list:
                data = self.__request(FunctionIds.GET_PRICE_BY_DATE, 3, fields,
                                      [[security, freq, start_date, end_date, fq]])
                df = convert_response_to_df(data, columns)
                ret[security] = df
            return ret
        except Exception as e:
            print(e)
            return {}


if __name__ == '__main__':
    import traceback

    try:
        script_file, host, port, investid, instanceid, frequency = sys.argv[1:]
        print(debug_print_prefix + ' got params', *sys.argv)
        api = TraderApiPy(script_file)
        globalVal.global_dict['api'] = api
        globalVal.global_dict['namespace'] = api.namespace
        api.set_frequency(frequency)
        api.Init(int(port))
        api.set_default_investid_instanceid(investid, instanceid)
        if frequency != 'tick':
            api.do_1m_scheduler()
        while True:
            time.sleep(3)
    except Exception as e:
        ne = traceback.format_exc()
        print(error_print_prefix + utils.get_formative_exception(ne))
